/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.service.impl;

import com.bifrost.common.model.ResultModel;
import com.bifrost.constant.AssociationType;
import com.bifrost.entity.*;
import com.bifrost.model.ItemModel;
import com.bifrost.model.OrderRequest;
import com.bifrost.model.vo.MerchantStoreVO;
import com.bifrost.model.vo.PageVO;
import com.bifrost.repository.CountryAreaRepository;
import com.bifrost.repository.MerchantStoreRepository;
import com.bifrost.repository.SkuAssociationRepository;
import com.bifrost.service.StoreService;
import com.bifrost.util.ServiceUtils;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author joewee
 * @version 1.0.0
 * @Date 2017/11/1 11:45
 */
@Service
public class StoreServiceImpl implements StoreService {

    private static final Logger LOGGER= LoggerFactory.getLogger(StoreServiceImpl.class);


    @Autowired
    MerchantStoreRepository merchantStoreRepository;
    @Autowired
    CountryAreaRepository countryAreaRepository;
    @Autowired
    SkuAssociationRepository skuAssociationRepository;

    /**
     * 查询所有门店
     *
     * @return
     */
    @Override
    public List<MerchantStore> getMerchantStores() {
        return merchantStoreRepository.findAll();
    }

    /**
     * 查询某区域内的门店
     *
     * @param areaId
     * @return
     */
    @Override
    public List<MerchantStore> getMerchantStores(Long areaId) {

        CountryArea area = countryAreaRepository.findOne(areaId);
        return merchantStoreRepository.findAllByStoreDistrict(area);
    }

    /**
     * 获取门店
     *
     * @param merchantId
     * @return
     */
    @Override
    public MerchantStore getMerchantStore(Long merchantId) {
        return merchantStoreRepository.findOne(merchantId);
    }
    /**
     * 获取门店详情
     *
     * @param merchantId
     * @return
     */
    @Override
    //@Cacheable(value = {"MerchantStore#600#10"})
    public ResultModel getMerchantStoreInfo(Long merchantId) {
        MerchantStore store = getMerchantStore(merchantId);
        if(store==null){
            return  ResultModel.error("未找到相关信息");
        }
        return ResultModel.ok("成功")
                .put("storeId",store.getMerchantId())
                .put("storePhone", store.getStorePhone())
                .put("storeName", store.getStoreName())
                .put("storeDesc", store.getDescription());
    }
    /**
     * 根据门店外部系统id获取门店
     *
     * @param id
     * @return
     */
    @Override
    public MerchantStore getMerchantStoreByExternalId(String id) {
        return merchantStoreRepository.findFirstByExternalId(id);
    }

    /**
     * 查询门店服务费计价策略
     *
     * @param merchantId 门店id
     * @return 返回计价策略MAX、SUM
     */
    @Override
    public String getMerchantStorePricingModel(Long merchantId) {
        MerchantStore store = getMerchantStore(merchantId);
        return store.getPriceAdjustmentMode();
    }

    /**
     * 检查门店是否提供订单请求的所需的所有服务
     */
    @Override
    public boolean canService(OrderRequest request) {
        Long shop = request.getServiceShop();
        if (!request.isNeedService()) {
            return true;
        }
        if (shop == null) {
            return false;
        }
        MerchantStore store = getMerchantStore(shop);
        List<Long> skus = request.getProducts().stream().map(ItemModel::getSkuId).collect(Collectors.toList());
        //所需服务
        List<ProductService> services = skus.stream().map(sku -> {
            return skuAssociationRepository.findAllByproductSkuIdAndAssociationType(sku, AssociationType.SERVICE);
        }).filter(list -> {
            return list != null;
        }).flatMap(List::stream).map(SkuAssociation::getProductService).collect(Collectors.toList());
        //提供的服务
        List<ProductService> offerServices = store.getServicePrices().stream().filter(servicePrice -> {
            return Boolean.TRUE.equals(servicePrice.getIsAvailable());
        }).map(ServicePrice::getProductService).collect(Collectors.toList());
        return services.stream().allMatch(prodcutService -> {
            boolean isTrue = offerServices.contains(prodcutService);
            return isTrue;
        });
    }

    /**
     * 检查门店是否提供订单中商品的所需的所有服务
     */
    @Override
    public boolean canService(Order order) {
        Long shop = order.getServiceShop();
        if (!Boolean.TRUE.equals(order.isNeedService())) {
            return true;
        }
        if (shop == null) {
            return false;
        }
        MerchantStore store = getMerchantStore(shop);
        List<Long> skus = order.getOrderItems().stream().map(OrderItem::getSkuId).collect(Collectors.toList());
        //所需服务
        List<ProductService> services = skus.stream().map(sku -> {
            return skuAssociationRepository.findAllByproductSkuIdAndAssociationType(sku, AssociationType.SERVICE);
        }).filter(list -> {
            return list != null;
        }).flatMap(List::stream).map(SkuAssociation::getProductService).collect(Collectors.toList());
        //提供的服务
        List<ProductService> offerServices = store.getServicePrices().stream().filter(servicePrice -> {
            return Boolean.TRUE.equals(servicePrice.getIsAvailable());
        }).map(ServicePrice::getProductService).collect(Collectors.toList());
        return services.stream().allMatch(prodcutService -> {
            boolean isTrue = offerServices.contains(prodcutService);
            return isTrue;
        });
    }

    /**
     * 获取后台门店
     *
     * @param page
     * @param pageSize
     * @param storeName
     * @param areaId
     * @param storeCode
     * @param externalIds
     * @return
     */
    @Override
    public Page<MerchantStore> getAdminMerchantStores(Integer page, Integer pageSize, String storeName, Long areaId, String storeCode, String externalIds) {
        Pageable pageable=new PageRequest(page - 1,pageSize,new Sort(Sort.Direction.DESC,"updatedTimestamp"));
        Specification<MerchantStore> query=new Specification<MerchantStore>() {
            @Override
            public Predicate toPredicate(Root<MerchantStore> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
                List<Predicate> predicates=new ArrayList<>();
                if (StringUtils.isNotEmpty(storeName)){
                    predicates.add(cb.like(root.get("storeName"),"%"+storeName+"%"));
                }
                if(areaId != null){
                    CountryArea area = countryAreaRepository.findOne(areaId);
                    if(area != null){
                        predicates.add(cb.equal(root.get("storeDistrict"),area));
                    }
                }
                ServiceUtils.findByExternalIds(predicates,root,cb,externalIds,false);
                if (StringUtils.isNotBlank(storeCode)){
                    CriteriaBuilder.In<String> in=cb.in(root.get("storeCode"));
                    String[] codes=storeCode.split(",");
                    for (int i=0;i<codes.length;i++){
                        in.value(codes[i]);
                    }
                    predicates.add(in);
                }
                Predicate[] p=new Predicate[predicates.size()];
                return cb.and(predicates.toArray(p));
            }
        };
        return merchantStoreRepository.findAll(query,pageable);
    }

    /**
     * 后台新增/更新门店
     *
     * @param store
     * @return
     */
    @Override
    public MerchantStore save(MerchantStore store) {
        return merchantStoreRepository.save(store);
    }

    /**
     * 后台删除门店
     *
     * @param merchantIds
     * @return
     */
    @Override
    public void deleteByMerchantId(String merchantIds) {
        if (StringUtils.isNotEmpty(merchantIds)){
            String[] stores=merchantIds.split(",");
            ArrayList<Long> storeIds=  new ArrayList<>(stores.length);
            try {
                Arrays.stream(stores).forEach(s -> {
                    storeIds.add(Long.valueOf(s));
                });
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.info("类型转换失败");
            }
            merchantStoreRepository.deleteByMerchantId(storeIds);

        }
        LOGGER.info("门店不能为空");
    }

    /**
     * 根据id和name查询当前用户所拥有的权限门店
     * @param user
     * @param merchantId
     * @param merchantName
     * @param merchantCode
     * @param pageCode
     * @param pageSize @return
     */
    @Override
    public PageVO havePermission(User user, Long merchantId, String merchantName, String merchantCode, Integer pageCode, Integer pageSize) {
        Pageable pageable = new PageRequest(pageCode-1,pageSize);
        Specification<MerchantStore> specification = getMerchantStoreSpecification(user, merchantId, merchantName, merchantCode);
        Page<MerchantStore> page = merchantStoreRepository.findAll(specification, pageable);
        List<MerchantStoreVO> merchantStoreVOList = Lists.newArrayList();
        if(CollectionUtils.isNotEmpty(page.getContent())) {
            page.getContent().forEach(merchantStore -> {
                MerchantStoreVO merchantStoreVO = new MerchantStoreVO();
                BeanUtils.copyProperties(merchantStore, merchantStoreVO);
                merchantStoreVO.setStoreCode(merchantStore.getExternalId());
                merchantStoreVOList.add(merchantStoreVO);
            });
        }
        Long totalElement = page.getTotalElements();
        PageVO pageVO = new PageVO(pageCode,pageSize,totalElement.intValue(),merchantStoreVOList);
        return pageVO;
    }

    @Override
    public List<MerchantStoreVO> havaPermissionWithUser(User user, Long merchantId, String merchantName, String merchantCode) {

        Specification<MerchantStore> specification = getMerchantStoreSpecification(user, merchantId, merchantName, merchantCode);
       List<MerchantStore> merchantStoreList =  merchantStoreRepository.findAll(specification);
        List<MerchantStoreVO> merchantStoreVOList = Lists.newArrayList();
        merchantStoreList.forEach(merchantStore -> {
            MerchantStoreVO merchantStoreVO=  new MerchantStoreVO();
            BeanUtils.copyProperties(merchantStore, merchantStoreVO);
            merchantStoreVO.setStoreCode(merchantStore.getExternalId());
            merchantStoreVOList.add(merchantStoreVO);
        });
        return merchantStoreVOList;
    }

    @Override
    public List<MerchantStore> getAllMerchantStores() {
        return merchantStoreRepository.getAll();
    }

    @NotNull
    private Specification<MerchantStore> getMerchantStoreSpecification(final User user, final Long merchantId, final String merchantName, final String merchantCode) {
        return new Specification<MerchantStore>() {
                @Override
                public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
                    List<Predicate> predicates = new ArrayList<>();
                    if(!StringUtils.isBlank(merchantName)){
                        //增加查询条件：门店名称
                        predicates.add(cb.like(root.get("storeName"),"%"+merchantName.trim()+"%"));
                    }
                    if(merchantId != null){
                        //增加查询条件：门店id
                        predicates.add(cb.equal(root.get("merchantId"),merchantId));
                    }
                    if(StringUtils.isNotBlank(merchantCode)){
                        //增加查询条件：门店编码
                        predicates.add(cb.like(root.get("storeCode"),"%"+merchantCode.trim()+"%"));
                    }
                    Set<MerchantStore> merchantStores = user.getMerchantStores();
                    if(CollectionUtils.isNotEmpty(merchantStores)){
                        CriteriaBuilder.In<Long> in = cb.in(root.get("merchantId"));
                        merchantStores.forEach(merchantStore -> {
                            in.value(merchantStore.getMerchantId());
                        });
                        predicates.add(in);
                    }
                    Predicate[] p = new Predicate[predicates.size()];
                    return cb.and(predicates.toArray(p));
                }
            };
    }

    /**
     * 获取门店基本信息
     *
     * @param merchantStoreId merchantStoreId
     * @return
     */
    @Override
    public MerchantStoreVO getMerchantStoreVO(Long merchantStoreId) {
        MerchantStore merchantStore = merchantStoreRepository.findOne(merchantStoreId);
        if( null != merchantStore){
            MerchantStoreVO vo = new MerchantStoreVO();
            vo.setMerchantId(merchantStore.getMerchantId());
            vo.setStoreCode(merchantStore.getStoreCode());
            vo.setStoreName(merchantStore.getStoreName());
            vo.setStorePhone(merchantStore.getStorePhone());
            CountryArea state = merchantStore.getStoreState();
            CountryArea city = merchantStore.getStoreCity();
            CountryArea district = merchantStore.getStoreDistrict();
            if(null != state && null != city && null != district)
                vo.setStoreAddr(state.getName() + city.getName() + district.getName() + merchantStore.getStoreStreet());
            else
                vo.setStoreAddr(merchantStore.getStoreStreet());
            return vo;
        }
        return null;
    }
}
